package cn.shuibo.advice;

import cn.shuibo.annotation.ResponseCrypt;
import cn.shuibo.config.MutiSecretKeyConfig;
import cn.shuibo.dto.ResponseBodyDTO;
import cn.shuibo.entity.SecretKeyEntity;
import cn.shuibo.exception.CryptErrorException;
import cn.shuibo.util.*;
import com.alibaba.fastjson.JSON;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Author:Bobby
 * DateTime:2019/4/9
 **/
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    private boolean encrypt;

    @Autowired
    private MutiSecretKeyConfig mutiSecretKeyConfig;

    private static ThreadLocal<Boolean> encryptLocal = new ThreadLocal<>();


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        encrypt = false;
        if (returnType.getMethod().isAnnotationPresent(ResponseCrypt.class) && mutiSecretKeyConfig.isOpen()) {
            encrypt = true;
        }
        return encrypt;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // EncryptResponseBodyAdvice.setEncryptStatus(false);
        // Dynamic Settings Not Encrypted
        Boolean status = encryptLocal.get();
        if (null != status && !status) {
            encryptLocal.remove();
            return body;
        }
        HttpHeaders headers = request.getHeaders();
        List<String> senderAppids = headers.get("senderAppid");
        SecretKeyEntity sender = null;
        if(AdviceLocalThread.getSenderAppid()!=null){
            SecretKeyEntity cache = mutiSecretKeyConfig.getByAppid(AdviceLocalThread.getSenderAppid());
            if(cache == null){
                throw new CryptErrorException("缓存的appid秘钥不存在:"+AdviceLocalThread.getSenderAppid());
            }
            sender = cache;
        }else {
            sender = mutiSecretKeyConfig.getCurrentAppid();
        }

        if (encrypt) {
            try {
                long endTime = System.currentTimeMillis();
                // 对返回内容进行加密
                String response_data = JSON.toJSONString(body);
                String aesKey = RandomUtils.getUUIDRandomTrim();//随机秘钥
                String response_content = AESUtils.encrypt(response_data, aesKey);//用随机秘钥对称加密返回数据
                Long time_stamp = System.currentTimeMillis();
                String response_content_digest = MD5Util.MD5Encode(response_data + time_stamp);//返回数据生成摘要
                String response_sign = "";//返回数据的数字签名
                String response_random_key = "";//加密后的随机秘钥
                try {
                    response_random_key = RSAUtils.encryptByPrivateKey(aesKey, sender.getPrivateKey());
                    response_sign = RSAUtils.encryptByPrivateKey(response_content_digest, sender.getPrivateKey());
                } catch (Exception e) {
                    log.error("对接口:【{}】,返回参数:【{}】加密失败:【{}】", e.getMessage());
                    throw new CryptErrorException("对接口:【{}】,返回参数:【{}】加密失败:【{}】");
                }
                Map<String, Object> retMap = new HashMap<>();
                retMap.put("appId", sender.getAppid());
                retMap.put("content", response_content);
                retMap.put("sign", response_sign);
                retMap.put("randomKey", response_random_key);
                retMap.put("timeStamp", time_stamp);
                retMap.put("interface", "/test01");//接口唯一标识
                ResponseBodyDTO result = ResponseBodyDTO.buildSuccess("OK", retMap);
                log.info("接口返回成功，目标类：{},方法：{},执行时间:{}");
                return result;
            } catch (Exception e) {
                log.error("Encrypted data exception", e);
            }
        }
        return body;
    }
}
